let W = null;
// 6.封装一个Watcher类
class Watcher {
  //Watcher实例化的时候接受两个参数,一个是key所在的对象 一个是key
  constructor(obj, key) {
    this.obj = obj
    this.key = key;
    // 当我要获取值的时候 把当前的watcher保存在全局变量上 方便defineReactive操作
    w = this
    this.get()
    w = null
  }
  //当组件内new Watcher的时候就是想得到一个值,所以封装一个get方法拿值
  get() {
    //一旦这样拿值,就会直接进入当前key的响应式的getter函数中
    return this.obj[this.key];
  }
  //在watcher内部封装一个update方法,当通知我更新的时候,直接调用当前的update方法
  update() {
    console.log(
      "gogogo,有人通知我了,我重新去请求数据是:" +
      this.get() +
      ",然后开始重新渲染视图!!!!"
    );
  }
}

// 5.定义一个dep类,用来收集依赖和通知更新
class Dep {
  constructor() {
    // 每次初始化Dep就会创建一个空的数组,然后用来保存所有的watcher
    this.subs = []
  }
  // 收集watcher 
  depend(watcher) {
    this.subs.push(watcher);
  }
  // 通知watcher更新
  notify() {
    this.subs.forEach((watcher) => {
      // 调用subs里所有的watcher更新
      watcher.update()
    })
  }
}
// 4.封装一个defineReactive函数,主要是为了的那个数据做数据劫持
function defineReactive(_data, key, value) {
  // 把value再次遍历 进行深层次的数据响应式
  observe(value)
  // 每次对一个值进行响应式的时候实例化一个Dep类
  const dep = new Dep()
  Object.defineProperty(_data, key, {
    get() {
      console.log('正在获取' + key + '的值');
      //数据被请求的时候,把观察者watcher收集起来到dep.subs中
      if (W) {
        dep.depend(W);
      }
      return value
    },
    set(newValue) {
      // 如果新的值等于旧的值 则直接return
      if (newValue === value) return
      console.log('正在设置' + key + "的值");
      value = newValue
      //当我的响应式数据被修改的时候,我要通知所有dep.subs里的watcher 进行更新 
      dep.notify();
    }
  })
}
// 3.封装一个Observer类,用来处理发布订阅模式的核心操作(响应式操作:收集依赖:通知更新)
class Observer {
  // constructor接受一个值 
  constructor(_data) {
    // 把这个对象放在实例上 将来在任务原型方法中都可以访问到这个实例上的数据
    // 判断当前数据是对象还是数组
    if (Array.isArray(_data)) {
      this.ServerObj(_data)
    } else {
      this.walk(_data)
    }
  }
  // 如果是数组的情况下 数据再次遍历
  ServerObj(_data) {
    _data.forEach((item) => {
      //把数组的值交给observe函数重新开始监听
      observe(item);
    });
  }
  // walk 如果是对象的情况下 接受传来的参数进行defineReactive数据劫持
  walk(_data) {
    for (let key of Object.keys(_data)) {
      //调用数据劫持的函数 并传参
      defineReactive(_data, key, _data[key])
    }
  }
}

// 2.封装observe函数,接受_data的值,一个个的进行数据劫持
function observe(_data) {
  // 只要它不是对象或为空的情况下
  if (typeof _data !== "object" || _data === null) return;

  new Observer(_data)
}

// 1.封装一个Vue的构造函数,接受一个配置对象
function Vue(options) {
  // 1.1 将options配置对象内的data,放在实例对象的this的_data属性上
  this._data = options.data
  // 进行数据代理 将_data中的属性取出来,一个个的放在Vue实例身上
  for (let key of Object.keys(this._data)) {
    // 使用这个方法把值放在Vue实例身上
    Object.defineProperty(this, key, {
      //给所有的属性添加了_data
      get() {
        return this._data[key]
      },
      // 设置的时候更改这个值
      set(newValuew) {
        this._data[key] = newValuew
      }
    })
  }
  observe(this._data)
}
// 实例化Vue
const vm = new Vue({
  data: {
    count: 0,
    Obj: { name: '罗飞', age: 18 },
    Ary: [
      "晴天", "雨天", "阴天"
    ]
  }
})
console.log(vm);
function huoqu() {
  //模拟模板解析,每个组件都会实例化一个watcher 然后开始观察数据
  new Watcher(vm, "Ary");
}
console.log(vm);